{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# Tool Windows: A Declarative Approach"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "Tool windows are child windows of the IDE that allow to display additional information or provide always-visible user interface elements. As seen in the [programmatic Tool Window example](tool_window.ipynb), the tool window elements can be configured in the Kotlin or Java code.\n",
    "\n",
    "Alternatively, we can use _a declarative_ approach by using plugin descriptor and an extension point.\n",
    "\n",
    "Let's mirror the previous example and create a simple tool window with the current time."
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "![Tool Window with current time](tool_window/tool_window_right.png)"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## Enabling IntelliJ Platform Integration"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-29T09:48:25.843672Z",
     "start_time": "2025-07-29T09:48:25.399294Z"
    }
   },
   "cell_type": "code",
   "source": "%use intellij-platform",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "IntelliJ Platform integration is loaded"
      ]
     },
     "metadata": {},
     "output_type": "display_data",
     "jetTransient": {
      "display_id": null
     }
    }
   ],
   "execution_count": 2
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "### Tool Window Content Class\n",
    "\n",
    " `CurrentTimeToolWindowContent` contains an actual user interface elements."
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-29T09:48:25.924743Z",
     "start_time": "2025-07-29T09:48:25.846127Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import java.awt.BorderLayout\n",
    "import java.time.ZonedDateTime\n",
    "import java.time.format.DateTimeFormatter\n",
    "import javax.swing.BorderFactory\n",
    "import javax.swing.JLabel\n",
    "import javax.swing.JPanel\n",
    "import javax.swing.SwingConstants\n",
    "\n",
    "class CurrentTimeToolWindowContent {\n",
    "    val contentPanel = JPanel()\n",
    "\n",
    "    private val currentTime = JLabel().apply {\n",
    "        horizontalAlignment = SwingConstants.CENTER\n",
    "    }\n",
    "\n",
    "    init {\n",
    "        with(contentPanel) {\n",
    "            layout = BorderLayout(0, 20)\n",
    "            border = BorderFactory.createEmptyBorder(40, 0, 0, 0)\n",
    "            add(currentTime, BorderLayout.CENTER)\n",
    "        }\n",
    "        updateCurrentTime()\n",
    "    }\n",
    "\n",
    "    fun updateCurrentTime() {\n",
    "        currentTime.text = ZonedDateTime.now().format(TIME_FORMATTER)\n",
    "    }\n",
    "\n",
    "    companion object {\n",
    "        private val TIME_FORMATTER = DateTimeFormatter.ofPattern(\"HH:mm\")\n",
    "    }\n",
    "}"
   ],
   "outputs": [],
   "execution_count": 3
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## Tool Window Factory"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "A tool window factory creates a user interface elements and registers it into an instance of `ToolWindow` provided by IntelliJ Platform API."
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-29T09:48:25.971707Z",
     "start_time": "2025-07-29T09:48:25.927031Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import com.intellij.openapi.project.DumbAware\n",
    "import com.intellij.openapi.project.Project\n",
    "import com.intellij.openapi.wm.ToolWindow\n",
    "import com.intellij.openapi.wm.ToolWindowFactory\n",
    "import com.intellij.ui.content.ContentFactory\n",
    "\n",
    "class CurrentTimeToolWindowFactory : ToolWindowFactory, DumbAware {\n",
    "    override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {\n",
    "        val toolWindowContent = CurrentTimeToolWindowContent()\n",
    "        val content = ContentFactory.getInstance().createContent(\n",
    "            toolWindowContent.contentPanel, \"\", false\n",
    "        )\n",
    "        toolWindow.contentManager.addContent(content)\n",
    "    }\n",
    "}"
   ],
   "outputs": [],
   "execution_count": 4
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Registering the Tool Window Factory\n",
    "\n"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "In a regular plugin, registering a tool window factory requires declaring an extension in the plugin descriptor.\n",
    "\n",
    "```xml\n",
    "<extensions defaultExtensionNs=\"com.intellij\">\n",
    "    <toolWindow id=\"Current Time\"\n",
    "                factoryClass=\"somePackage.CurrentTimeToolWindowFactory\"\n",
    "                anchor=\"right\"\n",
    "                icon=\"/general/information.svg\" />\n",
    "</extensions>\n",
    "```\n",
    "\n",
    "This XML declaration can be mirrored in Kotlin code. We will create an instance of the corresponding extension point.\n",
    "Then, we will register this instance using `registerExtension` function."
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-07-29T09:48:26.025594Z",
     "start_time": "2025-07-29T09:48:25.974811Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import com.intellij.openapi.extensions.ExtensionPointName\n",
    "import com.intellij.openapi.wm.ToolWindowAnchor\n",
    "import com.intellij.openapi.wm.ToolWindowEP\n",
    "\n",
    "runInEdt {\n",
    "    val toolWindowEP = ToolWindowEP().apply {\n",
    "        id = \"Current Time\"\n",
    "        factoryClass = CurrentTimeToolWindowFactory::class.java.name\n",
    "        anchor = ToolWindowAnchor.RIGHT.toString()\n",
    "        icon = \"AllIcons.General.Information\"\n",
    "        pluginDescriptor = notebookPluginDescriptor\n",
    "    }\n",
    "    val toolWindowEpName = ExtensionPointName.create<ToolWindowEP>(\"com.intellij.toolWindow\")\n",
    "\n",
    "    registerExtension(toolWindowEpName, toolWindowEP)\n",
    "}\n"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "There are few important aspects of this configuration.\n",
    "\n",
    "### Referring to a Tool Window Factory\n",
    "\n",
    "The `ToolWindowEP` instance of an extension point refers to a _factory class_. This is not an instance of the tool window factory class, but a fully qualified class name represented as a string.\n",
    "\n",
    "Since the instance is declared in this Kotlin Notebook, we will refer it by `class.java.name` member. This will provide a correct implicit package name and necessary technical class name prefixes provided by Kotlin Notebook integration.\n",
    "\n",
    "### Plugin Descriptor\n",
    "\n",
    "The extension point instance requires a reference to the plugin descriptor. While not necessary in XML, this is required in programmatic declaration. An instance of type-safe plugin descriptor is provided by the `notebookPluginDescriptor` variable. Additionally, this instance contains properly initialized classloaders that will load the tool window factory class.\n",
    "\n",
    "### Anchors and Icons\n",
    "\n",
    "Since we mirror the XML declarations, both anchor and icons are provided as strings. The `AllIcons.General.Information` refers to a constant provided by the Platform SDK.\n",
    "\n",
    "## Registering an extension\n",
    "\n",
    "The built-in function `registerExtension` will register an extension into the platform. Furthermore, it will ensure a proper lifecycle and disposing of tool window factories and tool windows when the Kernel is restarted."
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## Summary\n",
    "\n",
    "- Refer to the [Tool Window](tool_window.ipynb) example for basic concepts.\n",
    "- Use `::class.java.name` to refer to a fully qualified class name that is declared in the notebook.\n",
    "- Use `notebookPluginDescriptor` to refer to the plugin descriptor instance.\n",
    "- Use `registerExtension` to register an extension into the Platform with correct disposal management."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "name": "kotlin",
   "version": "2.2.20-dev-4982",
   "mimetype": "text/x-kotlin",
   "file_extension": ".kt",
   "pygments_lexer": "kotlin",
   "codemirror_mode": "text/x-kotlin",
   "nbconvert_exporter": ""
  },
  "ktnbPluginMetadata": {
   "sessionRunMode": "IDE_PROCESS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
